package org.sean.framework.web.filter;

import org.sean.framework.logging.Logger;
import org.sean.framework.util.StringUtil;
import org.sean.framework.web.interceptor.ApiInterceptor;
import org.springframework.core.NamedThreadLocal;
import org.springframework.core.Ordered;
import org.springframework.web.filter.OncePerRequestFilter;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.util.ContentCachingRequestWrapper;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Enumeration;

/**
 * 请求过滤器
 *
 * @author xielei
 */
public class LogFilter extends OncePerRequestFilter implements Ordered, HandlerInterceptor {

    /**
     * 请求
     */
    private static final ThreadLocal<HttpServletRequest> REQUESTS = new NamedThreadLocal<>("REQUEST");
    private static final String KEY_TIME = "k_time";
    protected final Logger log = Logger.newInstance(this.getClass());

    /**
     * 打印 HttpRequest
     *
     * @param request 请求
     */
    private void printRequest(HttpServletRequest request) {

        StringBuilder sb = new StringBuilder();
        sb.append(StringUtil.formatContent("\nAPI : {0}\n", request.getRequestURI()));
        sb.append(StringUtil.formatContent("METHOD : {0}\n", request.getMethod()));
        sb.append(StringUtil.formatContent("QUERY : {0}\n", StringUtil.transNull(request.getQueryString())));
        sb.append(StringUtil.formatContent("HEADERS : \n"));


        Enumeration<String> names = request.getHeaderNames();
        String name;
        while (names.hasMoreElements()) {
            name = names.nextElement();
            sb.append(StringUtil.formatContent("  {0}={1}\n", name, request.getHeader(name)));
        }
        sb.append(StringUtil.formatContent("  invokeIp={0}\n", request.getRemoteAddr()));
        log.info(sb.toString());
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {

        ContentCachingRequestWrapper wrappedRequest = new ContentCachingRequestWrapper(request);

        setRequestCharset(wrappedRequest);
        setResponseCharset(response);

        filterChain.doFilter(wrappedRequest, response);

    }

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        if (ApiInterceptor.isExecuted()) {
            request.setAttribute(KEY_TIME, System.currentTimeMillis());
            printRequest(request);
        }

        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) {
        REQUESTS.set(request);
        Long start = (Long) request.getAttribute(KEY_TIME);
        if (start != null) {
            long time = System.currentTimeMillis() - start;
            log.debug(() -> StringUtil.formatContent("{0}耗时: 毫秒({1})", time > 1000 ? "[严重警告]" : "", time));
        }
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
        request.removeAttribute(KEY_TIME);
        REQUESTS.remove();
    }

    /**
     * 设置请求字符集
     */
    private void setRequestCharset(HttpServletRequest httpServletRequest) {
        try {
            httpServletRequest.setCharacterEncoding(StandardCharsets.UTF_8.name());
        } catch (Exception e) {
            log.printStackTrace(e);
        }
    }


    /**
     * 设置响应字符集
     */
    private void setResponseCharset(HttpServletResponse response) {
        response.setCharacterEncoding(StandardCharsets.UTF_8.name());
    }

    /**
     * 原请求
     *
     * @return HttpServletRequest
     */
    public HttpServletRequest getRequest() {
        return REQUESTS.get();
    }

    @Override
    public int getOrder() {
        return Ordered.HIGHEST_PRECEDENCE + 1000;
    }

}